import type { AxiosProgressEvent, AxiosRequestConfig, AxiosResponse, GenericAbortSignal } from 'axios'
import request from './axios'
import { useAuthStore } from '@/store'
import { router } from '@/router';
import { ElMessage } from 'element-plus';

export interface HttpOption {
  url: string
  data?: any
  method?: string
  headers?: any
  onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void
  signal?: GenericAbortSignal
  beforeRequest?: () => void
  afterRequest?: () => void
}

export interface Response<T = any> {
  data: T;
  message: string | null;
  code: number;
}

export interface ErrorResponse {
  code: string;
  request: AxiosRequestConfig;
  response: AxiosResponse;
}

function http<T = any>(
  { url, data, method, headers, onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
) {
  const successHandler = (res: AxiosResponse<Response<T>>) => {
    if (res.data.code === 1 || typeof res.data === 'string') {
      return res.data;
    }

    return Promise.reject(res.data)
  }

  const failHandler = async (error: ErrorResponse) => {
    afterRequest?.()
    if (error.response.status === 403) {
      ElMessage({
        type: 'warning',
        message: error.response.data,
      });
    } else if (error.response.status === 401) {
      await router.push({ path: '/login' });
    }
    throw new Error(error?.response?.data || 'Error')
  }

  beforeRequest?.()

  method = method || 'GET'

  const params = Object.assign(typeof data === 'function' ? data() : data ?? {}, {});

  switch (method) {
    case 'GET':
      return request.get(url, { params, signal, onDownloadProgress }).then(successHandler, failHandler);
    case 'POST':
      return request.post(url, params, { headers, signal, onDownloadProgress }).then(successHandler, failHandler);
    case 'PUT':
      return request.put(url, params, { headers, signal, onDownloadProgress }).then(successHandler, failHandler);
    case 'DELETE':
      return request.delete(url, { params, signal, onDownloadProgress }).then(successHandler, failHandler);
    case 'PATCH':
      return request.patch(url, params, { headers, signal, onDownloadProgress }).then(successHandler, failHandler);
    default:
      return request.post(url, params, { headers, signal, onDownloadProgress }).then(successHandler, failHandler);
  }
}

export function get<T = any>(
  { url, data, method = 'GET', onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
): Promise<Response<T>> {
  return http<T>({
    url,
    method,
    data,
    onDownloadProgress,
    signal,
    beforeRequest,
    afterRequest,
  })
}

export function post<T = any>(
  { url, data, method = 'POST', headers, onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
): Promise<Response<T>> {
  return http<T>({
    url,
    method,
    data,
    headers,
    onDownloadProgress,
    signal,
    beforeRequest,
    afterRequest,
  })
}

export function deleteReq<T = any>(
  { url, data, method = 'DELETE', onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
): Promise<Response<T>> {
  return http<T>({
    url,
    method,
    data,
    onDownloadProgress,
    signal,
    beforeRequest,
    afterRequest,
  })
}

export function put<T = any>(
  { url, data, method = 'PUT', headers, onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
): Promise<Response<T>> {
  return http<T>({
    url,
    method,
    data,
    headers,
    onDownloadProgress,
    signal,
    beforeRequest,
    afterRequest,
  });
}

export function patch<T = any>(
  { url, data, method = 'PATCH', headers, onDownloadProgress, signal, beforeRequest, afterRequest }: HttpOption,
): Promise<Response<T>> {
  return http<T>({
    url,
    method,
    data,
    headers,
    onDownloadProgress,
    signal,
    beforeRequest,
    afterRequest,
  });
}

export default post
